package com.fx.spring.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

/**
 * @author ClanMy
 * 注意：Component注解不可缺失，否则工具类不生效
 */
@Component
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }

    /**
     * 获取applicationContext
     *
     * @return ApplicationContext
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 根据对象获取获取泛型对应的bean
     *
     * @param aClass 对象
     * @param index  泛型索引
     * @param <T>    泛型
     * @return bean
     */
    public static <T> Object getBean(Class<T> aClass, int index) {
        ParameterizedType parameterizedType = (ParameterizedType) aClass.getGenericSuperclass();
        Type type = parameterizedType.getActualTypeArguments()[index];
        return applicationContext.getBean((Class<?>) type);
    }

    /**
     * 根据class获取bean
     *
     * @param aClass class
     * @param <T>    泛型
     * @return bean
     */
    public static <T> T getBean(Class<T> aClass) {
        return applicationContext.getBean(aClass);
    }

    /**
     * 通过benName获取bean
     *
     * @param name beanName
     * @return bean
     */
    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    /**
     * 获取beanNames
     *
     * @return beanName数组
     */
    public static String[] getBeanDefinitionNames() {
        return applicationContext.getBeanDefinitionNames();

    }

    /**
     * 获取ben个数
     *
     * @return bean个数
     */
    public static int getBeanDefinitionCount() {
        return applicationContext.getBeanDefinitionCount();
    }

    /**
     * 获取指定被注解的类
     *
     * @param annotationType 注解类型
     * @return beanName数组
     */
    public static String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType) {
        return applicationContext.getBeanNamesForAnnotation(annotationType);
    }

    /**
     * 获取被指定注解的类
     *
     * @return 返回<beanName, bean> 集合
     */
    public static Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) {
        return applicationContext.getBeansWithAnnotation(annotationType);
    }

    /**
     * 获取指定类型对应的所有Bean，包括子类
     *
     * @param <T>  Bean类型
     * @param type 类、接口，null表示获取所有bean
     * @return 类型对应的bean，key是bean注册的name，value是Bean
     * @since 5.3.3
     */
    public static <T> Map<String, T> getBeansOfType(Class<T> type) {
        return applicationContext.getBeansOfType(type);
    }

    /**
     * 获取指定类型对应的Bean名称，包括子类
     *
     * @param type 类、接口，null表示获取所有bean名称
     * @return bean名称
     * @since 5.3.3
     */
    public static String[] getBeanNamesForType(Class<?> type) {
        return applicationContext.getBeanNamesForType(type);
    }

    /**
     * 获取配置文件配置项的值
     *
     * @param key 配置项key
     * @return 属性值
     * @since 5.3.3
     */
    public static String getProperty(String key) {
        return applicationContext.getEnvironment().getProperty(key);
    }

    /**
     * 获取当前的环境配置，无配置返回null
     *
     * @return 当前的环境配置
     * @since 5.3.3
     */
    public static String[] getActiveProfiles() {
        return applicationContext.getEnvironment().getActiveProfiles();
    }


    /**
     * 动态向Spring注册Bean
     * <p>
     * 由{@link org.springframework.beans.factory.BeanFactory} 实现，通过工具开放API
     *
     * @param <T>      Bean类型
     * @param beanName 名称
     * @param bean     bean
     */
    public static <T> void registerSingleton(String beanName, T bean) {
        if (applicationContext instanceof ConfigurableApplicationContext) {
            ((ConfigurableApplicationContext) applicationContext).getBeanFactory().registerSingleton(beanName, bean);
        }
    }

    /**
     * 关闭application
     */
    public static void close() {
        if (applicationContext instanceof ConfigurableApplicationContext) {
            ((ConfigurableApplicationContext) applicationContext).close();
        }
    }

}
